package net.idugic.rpc.socket.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import net.idugic.rpc.socket.common.Constants;
import net.idugic.rpc.socket.common.RPCException;
import net.idugic.rpc.socket.common.RPCRequest;
import net.idugic.rpc.socket.common.Serializer;

public class RPCClient {

	// private static Logger log = Logger.getLogger(RPCClient.class);

	private static RPCClient client = null;

	private RPCClient() {
	}

	public static RPCClient getInstance() {
		if (client == null) {
			client = new RPCClient();
		}
		return client;
	}

	public Object execute(String remoteCall, Object... parameters) throws RPCException {

		Object result = null;
		Socket socket = null;

		ObjectOutputStream out = null;
		ObjectInputStream in = null;

		try {
			RPCRequest request = new RPCRequest(remoteCall, parameters);
			// log.info("RPC request: " + request);

			socket = new Socket(Constants.SERVER_HOST, Constants.SERVER_PORT);
			socket.setSoTimeout(Constants.CLIENT_TIMEOUT);
			out = new ObjectOutputStream(socket.getOutputStream());

			// sending request...
			out.writeObject(request);
			out.flush();

			in = new ObjectInputStream(socket.getInputStream());

			// waiting for result...
			// long startTime = System.currentTimeMillis();
			// while (result == null) {
			// if (System.currentTimeMillis() > startTime
			// + Constants.CLIENT_TIMEOUT) {
			// throw new RPCException("Timeout in reading result.");
			// }
			// result = in.readObject();
			// }

			ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				byteOut.write(buf, 0, len);
			}
			byte[] resultInBytes = byteOut.toByteArray();
			result = Serializer.deserialize(resultInBytes);

			// log.info("RPC result: " + result);

			if (result instanceof Exception) {
				throw new RPCException("exception in remote call", (Exception) result);
			}

		} catch (Exception e) {
			// log.error("error in remote call", e);
			throw new RPCException(e);
		} finally {
			try {
				if (socket != null) {
					socket.close();
				}
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException e) {
				// log.error("error finalizing", e);
			}
		}
		return result;

	}
}
